import { Button, ConfigProvider, Drawer, Alert } from 'antd'
import zhCN from 'antd/locale/zh_CN'
import { uniqueId } from 'lodash-es'
import { createContext, useEffect, useRef, useState } from 'react'
import ReactDom from 'react-dom/client'

import { theme } from '@/utils/antdHelper'

const AekDrawerContext = createContext()

/**
 * 打开抽屉
 * 不可传入onSubmit和onCancel，因为PromiseThen会传入
 */
export function PromiseThen(props = {}) {
  const Component = this
  if (!Component.aekUuid) {
    Component.aekUuid = `promiseThen-uuid-${uniqueId()}`
  }
  const key = `drawerRoot-${Component.aekUuid}`
  return new Promise((resolve, reject) => {
    const wrapper = document.getElementById('drawerRoot')
    let child = document.getElementById(key)
    if (!child) {
      child = document.createElement('div')
      child.id = key
      wrapper.appendChild(child)
    }
    const root = ReactDom.createRoot(child)

    const onCancel = () => {
      root.unmount()
      reject(Error('关闭Drawer'))
    }
    const onSubmit = res => {
      root.unmount()
      resolve(res)
    }

    root.render(
      <ConfigProvider locale={zhCN} theme={theme}>
        <AekDrawerContext.Provider value={{ onCancel, onSubmit }}>
          <Component {...props} onSubmit={onSubmit} onCancel={onCancel} />
        </AekDrawerContext.Provider>
      </ConfigProvider>,
    )
  })
}

/**
 * @typedef {Object} OtherProps
 * @property {() => Promise} [onOk] 弹框点确认回调
 * @property {import("antd/es/button").ButtonProps} [okButtonProps] 确认按钮的属性
 * @property {boolean} [showFooter] 是否显示底部按钮
 * @property {({onOk: Promise, onClose: Function}) => React.ReactNode} [renderFooter] 构建footer
 * @property {() => void} [afterClose] 关闭弹窗之后的回调
 */

/**
 * @typedef {import("antd/es/drawer").DrawerProps & OtherProps} AekDrawerProps
 */

/**
 * 打开弹框
 * @param {AekDrawerProps} param0
 * @returns
 */
const AekDrawer = ({
  children,
  onOk: wrapperOnOk = async _ => _,
  okButtonProps = {},
  showFooter = true,
  renderFooter = () => undefined,
  afterClose = _ => _,
  ...otherProps
}) => {
  const ref = useRef({})
  const [confirmLoading, setConfirmLoading] = useState(false)
  const [visible, setVisible] = useState(false)

  useEffect(() => {
    setVisible(true)
  }, [])

  const onClose = () => {
    ref.current.clickOnClose = true
    setVisible(false)
  }

  const onOk = () => {
    setConfirmLoading(true)
    wrapperOnOk()
      .then(reason => {
        ref.current.reason = reason
        ref.current.clickOnOk = true
        setVisible(false)
      })
      .finally(() => {
        setConfirmLoading(false)
      })
  }

  return (
    <AekDrawerContext.Consumer>
      {props => {
        const { onCancel, onSubmit } = props
        return (
          <Drawer
            {...{
              open: visible,
              forceRender: true,
              maskClosable: false,
              ...otherProps,
              footer: showFooter ? (
                <div className='p12px flex-justify-end flex'>
                  {renderFooter({ onOk, onClose }) || otherProps.footer || (
                    <>
                      <Button onClick={onClose}>取消</Button>
                      <Button {...okButtonProps} type='primary' loading={confirmLoading} onClick={() => onOk()}>
                        确认
                      </Button>
                    </>
                  )}
                </div>
              ) : null,
              onClose,
              afterOpenChange: open => {
                // Drawer内部在afterClose还去改变了内部的state，所以让下面的延后执行，避免报错
                if (open) return
                setTimeout(() => {
                  if (ref.current.clickOnClose) {
                    onCancel()
                    afterClose()
                  }
                  ref.current.clickOnOk && onSubmit(ref.current.reason)
                }, 0)
              },
            }}
          >
            <Alert.ErrorBoundary>{children}</Alert.ErrorBoundary>
          </Drawer>
        )
      }}
    </AekDrawerContext.Consumer>
  )
}

/**@type {(props?: AekDrawerProps) => Promise<any>} */
AekDrawer.PromiseThen = PromiseThen

export default AekDrawer
